﻿using System;
using System.Collections.Generic;
using System.IO;
using GGEditor.API;

namespace GGEditor.Core.Plugins
{
	public class DefaultPluginLoader : IPluginLoader
	{
		private static IPluginLoader instance = new DefaultPluginLoader();

		private DefaultPluginLoader() { }

		public static IPluginLoader Instance
		{
			get { return instance; }
		}

		#region Implementation of IPluginLoader

		public void LoadDefaultPlugins()
		{
			LoadDependencyTree(PluginDependencyTree.FromFilePaths(DefaultPluginFilePaths));
		}

		public void LoadPluginsFromDirectory(string dirPath)
		{
			LoadDependencyTree(PluginDependencyTree.FromFilePaths(EnumeratePluginsInDirectory(dirPath)));
		}

		#endregion

		private bool LoadPlugin(string filePath)
		{
			
			return true;
		}

		private void LoadDependencyTree(IPluginDependencyTree depenencyTree)
		{
			if (!depenencyTree.IsValid)
				throw new PluginLoadDependencyException();

			IList<object> fileLoadingOrder = new List<object>();
			foreach (string filePath in depenencyTree.FileLoadingOrder)
			{
				fileLoadingOrder.Add(filePath);
			}

			int tries = fileLoadingOrder.Count;

			while (fileLoadingOrder.Count > 0 && tries > 0)
			{
				object fileLoadingItem = fileLoadingOrder[0];
				fileLoadingOrder.RemoveAt(0);

				try
				{
					// Assume the first item in the loading order list is a plugin
					IPlugin plugin = fileLoadingItem as IPlugin;

					// If that assumption is false, treat it as a string and
					// try to load the plugin with the string as the filePath
					if (fileLoadingItem is string)
					{
						string filePath = fileLoadingItem as string;
						plugin = XmlPluginLoader.Instance.Load(filePath);
					}

					if (plugin == null)
						throw new PluginLoadException();

					// Either way, once we get here we at least have a loaded plugin,
					// so replace whatever was the fileLoadingItem with the plugin
					// in case we need to put it at the end of the list.
					fileLoadingItem = plugin;

					// Try to add the plugin into the plugin tree
					PluginManager.Instance.AddPlugin(plugin);

					// If we got to here, everything worked for this plugin
					tries++;
				}
				catch (PluginLoadDependencyException e)
				{
					tries--;
					fileLoadingOrder.Add(fileLoadingItem);
					if (tries == 0)
						throw e;
				}
			}

			//if (fileLoadingOrder.Count > 0)
			//	throw new PluginLoadException();
		}

		private IEnumerable<string> EnumeratePluginsInDirectory(string dirPath)
		{
			if (!Directory.Exists(dirPath))
				yield break;

			foreach (string filePath in Directory.GetFiles(dirPath))
				if (filePath.EndsWith(".xml"))
					yield return filePath;
		}

		private IEnumerable<string> DefaultPluginFilePaths
		{
			get
			{
				foreach (string configDir in GlobalProperties.Instance.GetStringArray(
					"Environment.Paths.ConfigurationDirs"))
				{
					string pluginDir = Path.Combine(configDir, "Plugins");

					if (!Directory.Exists(pluginDir))
						continue;

					foreach (string filePath in EnumeratePluginsInDirectory(pluginDir))
						yield return filePath;

					foreach (string dirPath in Directory.GetDirectories(pluginDir))
						foreach (string filePath in EnumeratePluginsInDirectory(dirPath))
							yield return filePath;
				}
			}
		}
	}

	public interface IPluginLoader
	{
		void LoadDefaultPlugins();
		void LoadPluginsFromDirectory(string dirPath);
	}
}
